package com.share.common.utils;

import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 *  定位信息模型
 *  都来自org.springframework，redis的高版本支持者
 *
 * @author 01401061 - 孙艳强
 * @date 2020/12/31 17:41
 */

public class GeoUtil {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 一个个增加地理位置
     *
     * @param key       键值
     * @param longitude 经度
     * @param latitude  纬度
     * @param member    成员
     * @return 返回添加的行数 为 -1 则添加失败
     */
    public Long geoAdd(String key, Double longitude, Double latitude, Object member) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(longitude) || StringUtils.isEmpty(latitude) || StringUtils.isEmpty(member)) {
            return null;
        }
        Point point = new Point(longitude, latitude);
        return redisTemplate.opsForGeo().add(key, point, member);
    }

    /**
     * 一次性添加多个位置信息
     *
     * @param key                 键值
     * @param memberCoordinateMap 存放位置信息
     * @return 添加的行数 -1 为全部都添加失败
     */
    public Long geoAdd(String key, Map<Object, Point> memberCoordinateMap) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(memberCoordinateMap)) {
            return null;
        }
        return redisTemplate.opsForGeo().add(key, memberCoordinateMap);
    }

    /**
     * 获取两个地点之间的距离
     *
     * @param key     键值
     * @param member1 成员 1
     * @param member2 成员 2
     * @return 两个之间的距离, 单位为米（M/m） 如果成员不存在则返回{@literal null}
     */
    public Distance geoDistance(String key, Object member1, Object member2) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(member1) || StringUtils.isEmpty(member2)) {
            return null;
        }
        return redisTemplate.opsForGeo().distance(key, member1, member2);
    }


    /**
     * 返回geohash geohash：基本原理是将地球理解为一个二维平面，将平面递归分解成更小的子块，
     * 每个子块在一定经纬度范围内拥有相同的编码
     *
     * @param key     键值
     * @param members 成员值
     * @return 返回成员对应的geohash 如果成员不存在则geohash为null
     */
    public List<String> geoHash(String key, Object... members) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(members)) {
            return null;
        }
        return redisTemplate.opsForGeo().hash(key, members);
    }

    /**
     * 返回地点的经纬度
     *
     * @param key     键值
     * @param members 成员值
     * @return 返回成员对应的Point(经纬度) 如果成员不存在则为null
     */
    public List<Point> geoPosition(String key, Object... members) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(members)) {
            return null;
        }
        return redisTemplate.opsForGeo().position(key, members);
    }

    /**
     * 返回指定点 指定范围内存在的成员
     *
     * @param key    键值
     * @param radius 半径
     * @return GeoResults对象
     */
    public GeoResults<RedisGeoCommands.GeoLocation<Object>> geoRadius(String key, Double longitude, Double latitude, Double radius) {
        if (StringUtils.isEmpty(key) || longitude == null || latitude == null || radius == null) {
            return null;
        }
        Circle within = new Circle(new Point(longitude, latitude), radius);
        return redisTemplate.opsForGeo().radius(key, within);
    }


    /**
     * 返回改成员指定范围内中存在的其他成员 包含自身
     *
     * @param key    键值
     * @param member 成员
     * @param radius 距离 默认米
     * @return GeoResults对象
     */
    public GeoResults<RedisGeoCommands.GeoLocation<Object>> geoRadius(String key, Object member, Double radius) {
        if (StringUtils.isEmpty(key) || member == null || radius == null) {
            return null;
        }
        return redisTemplate.opsForGeo().radius(key, member, radius);
    }


    /**
     * 删除成员
     *
     * @param key     键值
     * @param members 成员 可变参数
     * @return 返回删除的个数 -1为删除失败
     */
    public Long geoRemove(String key, Object... members) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(members)) {
            return null;
        }
        return redisTemplate.opsForGeo().remove(key, members);
    }
}
